package com.scorpio.redis.cachelock;

import com.scorpio.redis.constant.RedisConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
public class RedisLock {
    private static final Logger log = LoggerFactory.getLogger(RedisLock.class);


    @Autowired
    private StringRedisTemplate redisTemplate;


    /**
     * 加锁 多个同时进行
     *
     * @param lockKeys
     * @param expire   超时/秒
     * @return
     */
    public boolean lock(List<String> lockKeys, int expire) {
        long recordTime = System.currentTimeMillis();
        long countDown = expire * 1000;
        while (true) {
            List<String> thisLockKeys = new ArrayList<>();
            for (String lockKey : lockKeys) {
                Boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, "");
                if (success) {
                    redisTemplate.expire(lockKey, expire, TimeUnit.SECONDS);
                    thisLockKeys.add(lockKey);
                }
            }
            if (thisLockKeys.size() == lockKeys.size()) {
                return true;
            }

            //没拿到锁 全部释放 从来
            unlocks(thisLockKeys);

            countDown -= Math.abs(System.currentTimeMillis() - recordTime);
            recordTime = System.currentTimeMillis();
            if (countDown <= 0) {
                //超时了
                return false;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
        }
    }

    /**
     * 解锁
     *
     * @param lockKeys
     */
    public void unlocks(List<String> lockKeys) {
        for (String lockKey : lockKeys) {
            unlock(lockKey);
        }
    }

    /**
     * 加锁
     *
     * @param lockKey
     * @param expire  超时/秒
     * @return
     */
    public boolean lock(String lockKey, int expire) {
        lockKey = RedisConstants.SYS_LOCK_KEY + lockKey;
        long recordTime = System.currentTimeMillis();
        long countDown = expire * 1000;
        while (true) {
            Boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, "");
            if (success) {
                redisTemplate.expire(lockKey, expire, TimeUnit.SECONDS);
                return true;
            }
            countDown -= Math.abs(System.currentTimeMillis() - recordTime);
            recordTime = System.currentTimeMillis();
            if (countDown <= 0) {
                //超时了
                return false;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
        }
    }

    /**
     * 解锁
     *
     * @param lockKey
     */
    public void unlock(String lockKey) {
        lockKey = RedisConstants.SYS_LOCK_KEY + lockKey;
        try {
            redisTemplate.delete(lockKey);
        } catch (Exception e) {
            log.error("【redis分布式锁】解锁异常, {}", e);
        }
    }

    public void lockRunable(String lockKey, Runnable runnable) {
        lockRunable(lockKey, 5, runnable);
    }

    /**
     * 执行锁同步任务
     *
     * @param lockKey
     * @param expire
     * @param runnable
     */
    public void lockRunable(String lockKey, int expire, Runnable runnable) {
        if (lock(lockKey, expire)) {
            try {
                runnable.run();
            } finally {
                unlock(lockKey);
            }
        } else {
            throw new RedisLockException();
        }
    }
}
